function validation(element, callback) {
    var bChecked        = '';
    var aryMultiThreads = Object();
    var fnIsActive      = function (obj, target, parent, msg, bChecked) {
        if (isVisibility(obj, target, parent)) {
            //console.log([obj, target, parent, msg, bChecked, setWarm(obj, target, parent, msg, bChecked)]);
            return setWarm(obj, target, parent, msg, bChecked);
        }
    };

    $(element).find("input, textarea, select").each(function () {
        var msg = '';
        //alert($(this).attr("id"));

        var rel      = getAttr($(this).attr('rel'), '', ' ', '');
        var message  = getAttr($(this).attr('message'), '', '', '');
        var note     = getAttr($(this).attr('note'), '', '', '');
        var data     = getAttr($(this).attr('data'), '', '', '');
        var target   = getAttr($(this).attr('target'), '', '', '');
        var parent   = getAttr($(this).attr('parent'), '', '', '');
        var linkwith = getAttr($(this).attr('linkwith'), '', '', '');
        var _min     = getAttr($(this).attr('min'), '', '', '');
        var _max     = getAttr($(this).attr('max'), '', '', '');
        resetWarm(this, target);

        if (($(this).hasClass('NotNull') == true/* || $(this).hasClass('SelOne')==true*/) && ($(this).val() == '' || $(this).val() == note)) {
            msg = getText("Compulsory", rel);
        } else if ($(this).hasClass('SelectOne') && !$("[name=" + linkwith + "]:checked").length > 0) {
            msg = (message == '' ? getText("Select one", rel) : message);
        } else if ($(this).hasClass('Check') && !$(this).is(':checked')) {
            msg = (message == '' ? getText("Check", rel) : message);
        } else if ($(this).val() != '') {
            if ($(this).hasClass('Tel') == true && checkTel($(this).val()) == false) {
                msg = getText("Invalid format", rel);
            } else if ($(this).hasClass('Email') == true && checkEmail($(this).val()) == false) {
                msg = getText("Invalid format", rel);
            } else if ($(this).hasClass('Num') == true && isFinite($(this).val()) == false) {
                msg = getText("Invalid format", rel);
            } else if ($(this).hasClass('EN') == true && checkEN($(this).val()) == false) {
                rel = getText("Letters", '');
                msg = getText("EN", rel);
            } else if ($(this).hasClass('Date') == true && checkDate($(this).val()) == false && $(this).val() != note) {
                msg = getText("Invalid format", rel);
            } else if ($(this).hasClass('File') == true && checkExtension($(this).val(), getAttr($(this).attr('format'), 'jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx,mp4', '', '')) == false) {
                msg = (message == '' ? getText("Invalid format", rel) : message);
            } else if ($(this).hasClass('Document') == true && checkExtension($(this).val(), getAttr($(this).attr('format'), 'pdf,doc,docx,xls,xlsx', '', '')) == false) {
                msg = (message == '' ? getText("Invalid format", rel) : message);
            } else if ($(this).hasClass('Media') == true && checkExtension($(this).val(), getAttr($(this).attr('format'), 'mp3,mp4', '', '')) == false) {
                msg = (message == '' ? getText("Invalid format", rel) : message);
            } else if ($(this).hasClass('Image') == true && checkExtension($(this).val(), getAttr($(this).attr('format'), 'jpg,jpeg,png,gif', '', '')) == false) {
                msg = (message == '' ? getText("Invalid format", rel) : message);
            } else if ($(this).hasClass('ConfirmData') == true && $(this).hasClass('Password') == true) {
                if ($(this).val() != $(linkwith).val()) {
                    if (rel.length == 0)
                        rel = getText("Confirm password", '');
                    msg = getText("Invalid data", rel);
                }
            } else if ($(this).hasClass('ConfirmData') == true) {
                if ($(this).val() != $(linkwith).val()) {
                    if (rel.length == 0)
                        rel = getText("Confirm data", '');
                    msg = getText("Invalid data", rel);
                }
            } else if ($(this).hasClass('PWO') == true) {
                aryMultiThreads["PW"] = $(this);
            } else if ($(this).hasClass('Password') == true) {
                var _fmin = 8;
                var _fmax = 24;
                if (checkNumber(_min) > 0 && _min < _fmax)
                    _fmin = _min
                if (checkNumber(_max) > 0 && _fmin < _max)
                    _fmax = _max

                if ($(this).val() == note || checkEN($(this).val()) == false || ($(this).val().length < _fmin || $(this).val().length > _fmax)) {
                    rel = getText("Letters", _fmin + "-" + _fmax);
                    msg = getText("EN", rel);
                }
            } else if ($(this).val().length < _min && checkNumber(_min) > 0 && $(this).val().length > _max && checkNumber(_max) > 0) {
                msg = getText("Letters", _fmin + "-" + _fmax);
            } else if ($(this).val().length < _min && checkNumber(_min) > 0) {
                msg = getText("Minimum letters", _min);
            } else if ($(this).val().length > _max && checkNumber(_max) > 0) {
                msg = getText("Maximum letters", _max);
            } else if ($(this).hasClass('Exist') == true) {
                aryMultiThreads[$(this).attr("id")] = this;
            } else if ($(this).hasClass('Captcha') == true) {
                aryMultiThreads["Captcha"] = this;
            }
        }

        tmpStr = fnIsActive(this, target, parent, msg, bChecked);
        if (tmpStr != null)
            bChecked = "#" + tmpStr;
    });

    //callback('');//for check
    if (!$.isEmptyObject(aryMultiThreads)) {
        var fnMultiCheck = function (tmpStr, ary, key) {
            if (tmpStr != "")
                bChecked = "#" + tmpStr;
            delete ary[key];

            if ($.isEmptyObject(ary)) {
                $(".warm.hidden").removeClass("hidden");
                if ($.isFunction(callback))
                    callback(bChecked);
            }
            return ary;
        }

        tempAry = aryMultiThreads;
        $.each(aryMultiThreads, function (i, v) {
            if (i == "Captcha") {
                checkCaptcha(v, function (tmpStr) {
                    aryMultiThreads = fnMultiCheck(tmpStr, aryMultiThreads, i);
                });
            } else if (i == "PW") {
                checkDataSame(v, function (tmpStr) {
                    aryMultiThreads = fnMultiCheck(tmpStr, aryMultiThreads, i);
                });
            } else {
                checkDataExist(v, function (tmpStr) {
                    aryMultiThreads = fnMultiCheck(tmpStr, aryMultiThreads, i);
                });
            }
        });
    } else {
        $(".warm.hidden").removeClass("hidden");
        if ($.isFunction(callback))
            callback(bChecked);
    }
}

function removeWarm(element) {
    $(element + " .warm").remove();
}

function resetWarm(obj, target) {
    if (target == "siblings")
        $(obj).parent().siblings().children(".warm").remove();
    else if (target == "parent")
        $(obj).parent().siblings(".warm").remove();
    else
        $(obj).siblings(".warm").remove();
}

function setWarm(obj, target, parent, msg, bChecked) {
    if (msg != '') {
        var _br = '';
        if (target == "curline")
            _br = ' style="float:right;"';
        msg = '<span>' + msg + '</span>';

        if (target == "siblings")
            $(obj).parent().siblings().append('<div class="warm hidden">' + msg + '</div>');
        else if (target == "parent")
            $(obj).parent().after('<div class="warm hidden">' + msg + '</div>');
        else
            $(obj).after('<div class="warm hidden"' + _br + '>' + msg + '</div>');

        if (bChecked == '') {
            if (parent != '')
                return parent;
            else
                return $(obj).attr('id');
        }
    } else {
        if (target == "siblings")
            $(obj).parent().siblings().children(".warm").remove();
        else if (target == "parent")
            $(obj).parent().siblings(".warm").remove();
        else
            $(obj).siblings(".warm").remove();
    }
    return null;
}

function isExist(id) {
    if (id.length > 0) {
        return true;
    } else {
        return false;
    }
}

function isVisibility(obj, target, parent) {
    if (target == 'parent')
        $obj = $(obj).parent();
    else if (parent != '')
        $obj = $('#' + parent);
    else
        $obj = $(obj);

    return $obj.is(":visible") && !$obj.is(":hidden");
    //return $(obj).parent().css("visibility")!="hidden" && $(obj).parent().parent().css("visibility")!="hidden" && $(obj).parent().parent().parent().css("visibility")!="hidden" && $(obj).parent().css("display")!="none" && $(obj).parent().parent().css("display")!="none" && $(obj).parent().parent().parent().css("display")!="none"
}

function checkNumber(_val) {
    if (isFinite(_val) == true) {
        return parseInt(_val);
    } else {
        return 0;
    }
}

function checkFloat(_val) {
    if (isFinite(_val) == true) {
        return parseFloat(_val);
    } else {
        return 0;
    }
}

function checkTel(_val) {
    _OK    = "0123456789-+()/, ";
    result = true;
    for (i = 1; i <= _val.length; i++) {
        ch = _val.charAt(i - 1);
        for (j = 1; j <= _OK.length; j++) {
            if (ch == _OK.charAt(j - 1))
                break;
            if (j == _OK.length) {
                result = false;
                break;
            }
        }
    }
    return result;
}

function checkEmail(_val) {
    var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
    return regex.test(_val);
}

function checkEN(_val) {
    var regex = /[^a-zA-Z0-9]/;
    return regex.test(_val) ? false : true;
}

function checkDate(_val) {
    var x = new Date(_val);
    return x && !isNaN(x) && (typeof x.getDate !== "undefined");
}

function checkFill(_obj, _status) {
    var note = _obj.attr('note');
    if (_status == 1) {
        if (_obj.val() == note) {
            _obj.val('');
            _obj.css('color', '#000');
        }
    } else {
        if (_obj.val() == '') {
            if (typeof note !== 'undefined' && note !== false) {
                _obj.val(note);
                _obj.css('color', '#777');
            }
        }
    }
}

function checkExtension(_val, _type) {
    var result = false;
    if (_val != "" && _type != "") {
        var extension = _val.substr((_val.lastIndexOf('.') + 1));
        result        = ($.inArray(extension, _type.split(",")) >= 0);
        //result=true;
    }
    return result;
}

function checkCaptcha(element, callback) {
    var bChecked = '';
    var rel      = getAttr($(element).attr('rel'), '', ' ', '');
    var target   = getAttr($(element).attr('target'), '', '', '');
    var parent   = getAttr($(element).attr('parent'), '', '', '');
    var mode     = getAttr($(element).attr('mode'), '', '', '');

    if (isVisibility(element, target, parent)) {
        resetWarm(element, target);

        $.post(gsPath + "my_captcha/check", {
            captcha: $(element).val(),
            mode:    mode
        }, function (result) {
            if (result['sResult'] != "true") {
                if (rel.length == 0)
                    rel = getText("Captcha", '');
                msg = getText("Invalid data", rel);

                tmpStr = setWarm(element, target, parent, msg, bChecked);
                if (tmpStr != null)
                    bChecked = tmpStr;
            }
            if ($.isFunction(callback))
                callback(bChecked);
        }, 'json');
    } else {
        if ($.isFunction(callback))
            callback();
    }
}

function checkDataSame(element, callback) {
    var bChecked = '';
    var rel      = getAttr($(element).attr('rel'), '', ' ', '');
    var target   = getAttr($(element).attr('target'), '', '', '');
    var parent   = getAttr($(element).attr('parent'), '', '', '');

    if (isVisibility(element, target, parent)) {
        //alert(JSON.stringify(element));
        resetWarm(element, target);
        $.post(gsPath + "/my_api/get_membership_data_same", {
            sData: $(element).val()
        }, function (result) {
            //alert(JSON.stringify(result));
            if (result['sResult'] == "false") {
                msg = getText("Data different", rel);

                tmpStr = setWarm(element, target, parent, msg, bChecked);
                if (tmpStr != null) bChecked = tmpStr;
            }
            if ($.isFunction(callback)) callback(bChecked);
        }, 'json');
    } else {
        if ($.isFunction(callback)) callback();
    }
}

function checkDataExist(element, callback) {
    var bChecked = '';
    var rel      = getAttr($(element).attr('rel'), '', ' ', '');
    var target   = getAttr($(element).attr('target'), '', '', '');
    var parent   = getAttr($(element).attr('parent'), '', '', '');

    if (isVisibility(element, target, parent)) {
        //alert(JSON.stringify(element));
        resetWarm(element, target);
        $.post(gsPath + "/my_api/get_membership_data_exist", {
            sField: $(element).attr('id'),
            sData:  $(element).val()
        }, function (result) {
            //alert(JSON.stringify(result));
            if (result['sResult'] == "true") {
                msg = getText("Data repeat", rel);

                tmpStr = setWarm(element, target, parent, msg, bChecked);
                if (tmpStr != null)
                    bChecked = tmpStr;
            }
            if ($.isFunction(callback))
                callback(bChecked);
        }, 'json');
    } else {
        if ($.isFunction(callback))
            callback();
    }
}

function getAttr(val, defaultVal, code1, code2) {
    var tempStr = defaultVal;
    if (typeof val !== 'undefined' && val !== false) {
        if (val.length > 0)
            tempStr = code1 + val + code2;
    }
    return tempStr;
}

function getNote() {
    $('[note]').each(function () {
        checkFill($(this), 0);

        $(this).bind("focus", function () {
            checkFill($(this), 1);
        });

        $(this).bind("blur", function () {
            checkFill($(this), 0);
        });
    });
}

// String:date String:format return true/false
// date= 1/1/1 , 1-1-1
// format= DMY, MDY, YMD
function checkDate_v2(date, format) {
    try {
        var regex = /^[\d]{1,2}[/-][\d]{1,2}[/-][\d]{1,}$/;
        if (!regex.test(date)) {
            console.log("Not / or - format, Or not a date");
            return false;
        }
        var sign  = date.match(/[/-]/);
        var sign2 = date.match(/[/-]/g);
        if (sign != sign2[1]) {
            console.log("/ and - cannot use together");
            return false;
        }
        if (!(format == "DMY" || format == "MDY" || format == "YMD")) {
            format = "DMY";
        }
        //re-order the date, month and year
        var arr = date.split(/[/-]/);
        if (format == "MDY") {
            var temp = arr[0];
            arr[0]   = arr[1];
            arr[1]   = temp;
        }
        if (format == "YMD") {
            var temp = arr[0];
            arr[0]   = arr[2];
            arr[2]   = temp;
        }
        // check date valid
        arr[0] = parseInt(arr[0]);
        if (arr[0] > 31 || arr[0] == 0) {
            console.log("Date cannot > 31 or = 0");
            return false;
        }
        // check month valid
        arr[1] = parseInt(arr[1]);
        if (arr[1] > 12 || arr[1] == 0) {
            console.log("Month cannot > 12 or = 0");
            return false;
        }
        // even month is only 31 days but Auguest and February are special case
        if (arr[1] != 8 || arr[1] != 2) {
            if (arr[1] % 2 == 0) {
                if (arr[0] >= 31) {
                    console.log("That month is no 31 days");
                    return false;
                }
            }
        }
        // February is only 29 days
        if (arr[1] == 2) {
            if (arr[0] > 29) {
                console.log("February is no 30 days");
                return false;
            }
            // check 28 days February case
            if (arr[2] % 100 == 0) {
                if (arr[2] % 400 != 0) {
                    if (arr[1] >= 29) {
                        console.log("That year of February is no 29 days");
                        return false;
                    }
                }
            } else if (arr[2] % 4 != 0) {
                if (arr[0] >= 29) {
                    console.log("That year of February is no 29 days");
                    return false;
                }
            }
        }
        console.log(true);
        return true;
    } catch (err) {
        console.log(err.message);
    }
}

// String: date, format, min, max return true/false
// format= DMY, MDY, YMD , if '' = DMY
// min = [D]/[M]/[Y], [D]-[M]-[Y], if '' = 0/0/0
// max = [D]/[M]/[Y], [D]-[M]-[Y], if '' return false
function checkDateInRange(date, format, min, max) {
    if (!checkDate(date, format)) {
        return false;
    }
    var d     = date.split(/[/-]/);
    var regex = /^[\d]{1,2}[/-][\d]{1,2}[/-][\d]{1,}$/;
    var minDate, minMonth, minYear, maxDate, maxMonth, maxYear;
    if (!(format == "DMY" || format == "MDY" || format == "YMD")) {
        format = "DMY";
    }
    //re-order the date, month and year
    if (format == "MDY") {
        var temp = d[0];
        d[0]     = d[1];
        d[1]     = temp;
    }
    if (format == "YMD") {
        var temp = d[0];
        d[0]     = d[2];
        d[2]     = temp;
    }
    // if min = '' set to 0/0/0
    if (!regex.test(min)) {
        console.log("Min date is null or wrong date, set to today");
        var today = new Date();
        minDate   = today.getDate();
        minMonth  = (today.getMonth() + 1);
        minYear   = today.getFullYear();
    } else {
        var arr  = min.split(/[/-]/);
        minDate  = arr[0];
        minMonth = arr[1];
        minYear  = arr[2];
    }
    // check max is ''
    if (!regex.test(max)) {
        console.log("Max date not / or - format, Or not a date");
        return false;
    } else {
        var arr  = max.split(/[/-]/);
        maxDate  = arr[0];
        maxMonth = arr[1];
        maxYear  = arr[2];
    }
    // check date in range
    if (!(parseInt(d[0]) >= minDate && parseInt(d[0]) <= maxDate)) {
        console.log("Date is not in range");
        return false;
    }
    if (!(parseInt(d[1]) >= minMonth && parseInt(d[1]) <= maxDate)) {
        console.log("Month is not in range");
        return false;
    }
    if (!(parseInt(d[2]) >= minYear && parseInt(d[2]) <= maxYear)) {
        console.log("Year is not in range");
        return false;
    }
    console.log("In range");
    return true;
}